package jass.generators;

/** Reson filter.
    @author Kees van den Doel (kvdoel@cs.ubc.ca)
*/

public class ResonFilter implements Filter {

    static final double cosh(double x) {
        return (Math.exp(x) + Math.exp(-x))/2;
    }

    static final double sinh(double x) {
        return (Math.exp(x) - Math.exp(-x))/2;
    }
        
    /** Sampling rate in Hertz. */
    protected float srate;
    
    /** State of filter. */
    private float yt_1, yt_2;

    /** Cached value. */
    private float c_i;
    
    /** The transfer function of a reson filter is H(z) = 1/(1-twoRCosTheta/z + R2/z*z). */
    private float R2;

    /** The transfer function of a reson filter is H(z) = 1/(1-twoRCosTheta/z + R2/z*z). */
    private float twoRCosTheta;

    /** Reson filter gain. */
    private float ampR; 

    /** Create and initialize.
        @param srate sampling rate in Hertz.
     */
    public ResonFilter(float srate) {
        this.srate = srate;
        reset();
    }

    /** Set the reson coefficients and gain. Cache c_i.
        @param f resonance frequency in Hertz.
        @param d damping in radians/s.
        @param a gain.
     */
    public void setResonCoeff(float f, float d, float a) {
        float tmp_r = (float)(Math.exp(-d/srate));
        R2 = tmp_r*tmp_r;
        if(f > 0) {
            twoRCosTheta = (float)(2*Math.cos(2*Math.PI*f/srate)*tmp_r);
            c_i = (float)(Math.sin(2*Math.PI*f/srate)*tmp_r);
        } else {
            twoRCosTheta = (float)(cosh(2*Math.PI*f/srate)*tmp_r);
            //c_i = (float)(sinh(-2*Math.PI*f/srate)*tmp_r);
            c_i = 1;
        }
        setGain(a);
    }
    
    /** Set gain.
        @param a gain.
    */
    public void setGain(float a) {
        ampR = a * c_i;
    }
    
    /** Reset state.
     */
    public void reset() {
        yt_1 = yt_2 = 0;
    }
    
    /** Proces input (may be same as output).
        @param output user provided buffer for returned result.
        @param input user provided input buffer.
        @param nsamples number of samples written to output buffer.
        @param inputOffset where to start in circular buffer input.
    */
    public void filter(float [] output, float[] input, int nsamples, int inputOffset) {
        float ynew=0;
        if(inputOffset == 0) {
            for(int k=0;k<nsamples;k++) {
                ynew = twoRCosTheta * yt_1 - R2 * yt_2 + ampR * input[k];
                yt_2 = yt_1;
                yt_1 = ynew;
                output[k] = ynew;
            }
//            System.out.println(R2+" "+ynew);
        } else {
            int inputLen = input.length;
            int ii = inputOffset;
            for(int k=0;k<nsamples;k++) {
                ynew = twoRCosTheta * yt_1 - R2 * yt_2 + ampR * input[ii];
                yt_2 = yt_1;
                yt_1 = ynew;
                output[k] = ynew;
                if(ii == inputLen - 1) {
                    ii = 0;
                } else {
                    ii++;
                }
            }
        }
    }

    /** Proces a single sample. Use thgis to use ResonFilter in stand-alone mode (i.e., not
	in a patch.
        @return returned result.
        @param input user provided input sample
    */
    public float filter1Sample(float input) {
        float ynew=0;
	ynew = twoRCosTheta * yt_1 - R2 * yt_2 + ampR * input;
	yt_2 = yt_1;
	yt_1 = ynew;
	return ynew;
    }
}
